﻿using System;
using System.Globalization;
using System.Xml;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Metadata;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.SdkTypeProxy.Metadata;

namespace CSP.CRM.Plugins
{
  public sealed class LinkIncidentToProcedure : IPlugin 
  {
    private IPluginExecutionContext context;
    private ICrmService crm;
    private IMetadataService meta;

    private int procedureTypeValue = 200555;

    
    void IPlugin.Execute(IPluginExecutionContext context)
    {
      this.context = context;
      crm = context.CreateCrmService(true);

      if (!context.InputParameters.Properties.Contains("Target"))
        return;
      if (!(context.InputParameters.Properties["Target"] is DynamicEntity))
        return;

      DynamicEntity procedure = (DynamicEntity)context.InputParameters.Properties["Target"];
      if (!procedure.Name.StartsWith("csp_", StringComparison.OrdinalIgnoreCase))
        throw new InvalidPluginExecutionException("Error on CSP plugin: <b>" + procedure.Name + "</b> entity not supported.");

      Guid procedureId = (Guid)context.OutputParameters[ParameterName.Id];
      Guid incidentId = FetchIncidentId(procedure.Name, procedureId);
      if (incidentId != Guid.Empty) // Incident already exists
        SetProcedureId(incidentId, procedureId);
      else // Incident not yet created
      {
        incidentId = CreateIncident(procedureId, procedure.Name, procedure);
        SetIncidentId(procedure.Name, procedureId, incidentId);
      }
        
    }

    // Sets procedureid on a given incident record: incident.csp_procedureid = procedureId
    private void SetProcedureId(Guid incidentId, Guid procedureId)
    {
      DynamicEntity incident = new DynamicEntity("incident");
      // Add case identifier property
      KeyProperty propIncidentKey = new KeyProperty("incidentid", new Key(incidentId));
      incident.Properties.Add(propIncidentKey);
      // Add string property pointing to procedure
      StringProperty propProcedureId = new StringProperty("csp_procedureid", procedureId.ToString());
      incident.Properties.Add(propProcedureId);
      // Create target
      TargetUpdateDynamic tupd = new TargetUpdateDynamic();
      tupd.Entity = incident;
      UpdateRequest upr = new UpdateRequest();
      upr.Target = tupd;
      // Execute request
      crm.Execute(upr);
    }

    // Sets incidentid on a given procedure record: procedure.csp_procedureid = incidentId
    private void SetIncidentId(string procedureName, Guid procedureId, Guid incidentId)
    {
      DynamicEntity procedure = new DynamicEntity(procedureName);
      // Add procedure identifier property
      KeyProperty propProcedureId = new KeyProperty(procedureName + "id", new Key(procedureId));
      procedure.Properties.Add(propProcedureId);
      // Add lookup property pointing to incident
      LookupProperty propIncidentId = new LookupProperty("csp_incidentid", new Lookup("incident", incidentId));
      procedure.Properties.Add(propIncidentId);
      // Create target
      TargetUpdateDynamic tupd = new TargetUpdateDynamic();
      tupd.Entity = procedure;
      UpdateRequest upr = new UpdateRequest();
      upr.Target = tupd;
      // Execute request
      crm.Execute(upr);
    }
    
    // Retrieves the csp_incidentid of a given procedure record
    private Guid FetchIncidentId(string procedureName, Guid procedureId)
    {
      string fetchXml = @"<fetch mapping='logical' count='1'>
                            <entity name='{0}'>
                              <attribute name='csp_incidentid' />
                              <filter>
                                <condition attribute='{0}id' operator='eq' value='{1}' />
                              </filter>
                            </entity>
                          </fetch>";
      fetchXml = String.Format(CultureInfo.InvariantCulture, fetchXml, procedureName, procedureId);
      string resultXml = (string)crm.Fetch(fetchXml);
      XmlDocument resultDom = new XmlDocument();
      resultDom.LoadXml(resultXml);
      XmlNode textNode = resultDom.SelectSingleNode("/resultset/result/csp_incidentid/text()");
      if (textNode != null)
        return new Guid(textNode.Value);
      else
        return Guid.Empty;
    }

    private int GetEntityTypeCode(string entityName)
    {
      meta = context.CreateMetadataService(true);
      RetrieveEntityRequest req = new RetrieveEntityRequest();
      req.EntityItems = EntityItems.EntityOnly;
      req.LogicalName = entityName;
      RetrieveEntityResponse resp = (RetrieveEntityResponse)meta.Execute(req);
      if (resp.EntityMetadata != null)
        return resp.EntityMetadata.ObjectTypeCode.Value;
      else
        throw new InvalidPluginExecutionException("Entity " + entityName + " not found");
    }

    // Create incident and attach it to the procedure
    private Guid CreateIncident(Guid procedureId, string procedureName, DynamicEntity procedure)
    {
      Lookup luContact = (Lookup)procedure["csp_contactid"];
      DynamicEntity incident = new DynamicEntity("incident");
      StringProperty propProcedureId = new StringProperty("csp_procedureid", procedureId.ToString());
      incident.Properties.Add(propProcedureId);
      string incidentTitle = (string)procedure["csp_title"];
      StringProperty propTitle = new StringProperty("title", incidentTitle);
      incident.Properties.Add(propTitle);
      PicklistProperty propCaseType = new PicklistProperty("casetypecode", new Picklist(procedureTypeValue));
      incident.Properties.Add(propCaseType);
      int procedureTypeCode = GetEntityTypeCode(procedureName);
      PicklistProperty propProcedureType = new PicklistProperty("csp_proceduretype", new Picklist(procedureTypeCode));
      incident.Properties.Add(propProcedureType);
      string strFollowUpBy = DateTime.Now.AddDays(5).ToString("yyyy-MM-dd");
      CrmDateTimeProperty propFollowUpBy = new CrmDateTimeProperty("followupby", new CrmDateTime(strFollowUpBy));
      incident.Properties.Add(propFollowUpBy);
      CustomerProperty propContact = new CustomerProperty("customerid", new Customer("contact", luContact.Value));
      incident.Properties.Add(propContact);
      return crm.Create(incident);
    }


  }
}
